/*-
 * Copyright (c) 2012-2013 Shang-Rong Cai. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $ MTBLAS: mtblas_exec.c    Rev. 0.0.8    2013-10-22T15:47+08:00 $
 */

#include <stdlib.h>
#ifdef DEBUG_INFO
#include <stdio.h>
#endif

#include <exec_isa.h>


void execute(void *oc, const size_t sz, operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    size_t zi;  /* size_t based loop counter or index */
    void (*opcode)(operand_t*, operand_t*, operand_t*, operand_t*);

    opcode = oc;

    for (zi = 0; zi < sz; zi++)
    {
        (*opcode)(ra, rb, rc, rd);
    }
}


#ifdef DEBUG_INFO
/*
 * Combine execute() and opcode
 *
 * ra: opchunk_t; target address of execution: opcode, counts, operands.
 * rb: opchunk_t; for modifying target, update target before executing ra.
 * rc: opchunk_t; set/reset address value (change after run)
 * rd: opchunk_t; set/reset step value (change after run)
 *
 * Remark: 
 * The modifying target will be update from the outer to inner when using this 
 * opocde nestedly.  The outer will update before running inner.
 *
 * This opcode is usually the same as op4_exec_op() but with metadata output.
 */
void debug_op4_exec_op(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    addr_t oc;
    size_t sz;

    operand_t op_a;
    operand_t op_b;
    operand_t op_c;
    operand_t op_d;

    opchunk_t *pk_a;
    opchunk_t *pk_b;
    opchunk_t *pk_c;
    opchunk_t *pk_d;

    printf("--op4_exec_op_debug()---------------------------------------\n");

    pk_a = (opchunk_t*) ra->addr;
    pk_b = (opchunk_t*) rb->addr;
    pk_c = (opchunk_t*) rc->addr;
    pk_d = (opchunk_t*) rd->addr;

    printf("ADDRESS: pk_a = %p\n", pk_a);
    printf("ADDRESS: pk_b = %p\n", pk_b);
    printf("ADDRESS: pk_c = %p\n", pk_c);
    printf("ADDRESS: pk_d = %p\n", pk_d);

    /* Modifying Target */
    pk_b->oc = pk_c->oc;
    pk_b->sz = pk_c->sz;

    pk_b->ra.addr = pk_c->ra.addr;
    pk_b->ra.step = *((size_t*) pk_d->ra.addr);
    pk_b->rb.addr = pk_c->rb.addr;
    pk_b->rb.step = *((size_t*) pk_d->rb.addr);
    pk_b->rc.addr = pk_c->rc.addr;
    pk_b->rc.step = *((size_t*) pk_d->rc.addr);
    pk_b->rd.addr = pk_c->rd.addr;
    pk_b->rd.step = *((size_t*) pk_d->rd.addr);

    printf("MODIFY TARGET (rb):\n");
    printf("ADDRESS: pk_b->oc = %p\n", pk_b->oc);
    printf("ADDRESS: pk_b->sz = %zd\n", pk_b->sz);

    printf("ADDRESS: pk_b->ra.addr = %p; pk_b->ra.step = %zd\n", pk_b->ra.addr, pk_b->ra.step);
    printf("ADDRESS: pk_b->rb.addr = %p; pk_b->rb.step = %zd\n", pk_b->rb.addr, pk_b->rb.step);
    printf("ADDRESS: pk_b->rc.addr = %p; pk_b->rc.step = %zd\n", pk_b->rc.addr, pk_b->rc.step);
    printf("ADDRESS: pk_b->rd.addr = %p; pk_b->rd.step = %zd\n", pk_b->rd.addr, pk_b->rd.step);

    printf("BEFORE EXECUTE OPCODE (ra):\n");
    printf("ADDRESS: pk_a->oc = %p\n", pk_a->oc);
    printf("ADDRESS: pk_a->sz = %zd\n", pk_a->sz);

    printf("ADDRESS: pk_a->ra.addr = %p; pk_a->ra.step = %zd\n", pk_a->ra.addr, pk_a->ra.step);
    printf("ADDRESS: pk_a->rb.addr = %p; pk_a->rb.step = %zd\n", pk_a->rb.addr, pk_a->rb.step);
    printf("ADDRESS: pk_a->rc.addr = %p; pk_a->rc.step = %zd\n", pk_a->rc.addr, pk_a->rc.step);
    printf("ADDRESS: pk_a->rd.addr = %p; pk_a->rd.step = %zd\n", pk_a->rd.addr, pk_a->rd.step);

    /* Setup Target Run */
    oc = pk_a->oc;
    sz = pk_a->sz;

    op_a.addr = pk_a->ra.addr;
    op_a.step = pk_a->ra.step;
    op_b.addr = pk_a->rb.addr;
    op_b.step = pk_a->rb.step;
    op_c.addr = pk_a->rc.addr;
    op_c.step = pk_a->rc.step;
    op_d.addr = pk_a->rd.addr;
    op_d.step = pk_a->rd.step;

    execute(oc, sz, &op_a, &op_b, &op_c, &op_d);

    printf("AFTER EXECUTE OPCODE (ra):\n");
    printf("ADDRESS: pk_a->oc = %p\n", pk_a->oc);
    printf("ADDRESS: pk_a->sz = %zd\n", pk_a->sz);

    printf("ADDRESS: pk_a->ra.addr = %p; pk_a->ra.step = %zd\n", pk_a->ra.addr, pk_a->ra.step);
    printf("ADDRESS: pk_a->rb.addr = %p; pk_a->rb.step = %zd\n", pk_a->rb.addr, pk_a->rb.step);
    printf("ADDRESS: pk_a->rc.addr = %p; pk_a->rc.step = %zd\n", pk_a->rc.addr, pk_a->rc.step);
    printf("ADDRESS: pk_a->rd.addr = %p; pk_a->rd.step = %zd\n", pk_a->rd.addr, pk_a->rd.step);

    /* Update Modifiers Address */
    pk_c->ra.addr += pk_c->ra.step;
    pk_c->rb.addr += pk_c->rb.step;
    pk_c->rc.addr += pk_c->rc.step;
    pk_c->rd.addr += pk_c->rd.step;

    printf("ADDRESS MODIFIER (rc):\n");
    printf("ADDRESS: pk_c->ra.addr = %p; pk_c->ra.step = %zd\n", pk_c->ra.addr, pk_c->ra.step);
    printf("ADDRESS: pk_c->rb.addr = %p; pk_c->rb.step = %zd\n", pk_c->rb.addr, pk_c->rb.step);
    printf("ADDRESS: pk_c->rc.addr = %p; pk_c->rc.step = %zd\n", pk_c->rc.addr, pk_c->rc.step);
    printf("ADDRESS: pk_c->rd.addr = %p; pk_c->rd.step = %zd\n", pk_c->rd.addr, pk_c->rd.step);

    /* Update Modifiers Step */
    pk_d->ra.addr += pk_d->ra.step;
    pk_d->rb.addr += pk_d->rb.step;
    pk_d->rc.addr += pk_d->rc.step;
    pk_d->rd.addr += pk_d->rd.step;

    printf("STEP MODIFIER (rd):\n");
    printf("ADDRESS: pk_d->ra.addr = %p; pk_d->ra.step = %zd\n", pk_d->ra.addr, pk_d->ra.step);
    printf("ADDRESS: pk_d->rb.addr = %p; pk_d->rb.step = %zd\n", pk_d->rb.addr, pk_d->rb.step);
    printf("ADDRESS: pk_d->rc.addr = %p; pk_d->rc.step = %zd\n", pk_d->rc.addr, pk_d->rc.step);
    printf("ADDRESS: pk_d->rd.addr = %p; pk_d->rd.step = %zd\n", pk_d->rd.addr, pk_d->rd.step);

    /* Update Operands Address */
    ra->addr += ra->step;
    rb->addr += rb->step;
    rc->addr += rc->step;
    rd->addr += rd->step;

    printf("------------------------------------------------------------\n");
}


void debug_print_operands_address(size_t thrz, char *msg, operand_t *ra)
{
    size_t zi;

    for (zi = 0; zi < thrz; zi++)
    {
        printf("[%2zd]: %s ADDRESS: %p\n", zi, msg, &ra[zi]);
    }
}


void debug_print_operands_data(size_t thrz, char *msg, operand_t *ra)
{
    size_t zi;

    for (zi = 0; zi < thrz; zi++)
    {
        printf("[%2zd]: %s  addr->%p  step->%d\n", zi, msg, ra[zi].addr, ra[zi].step);
    }
}
#endif
